/*
* Copyright (c) 2007, Stanisław Pitucha
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY Stanisław Pitucha ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Stanisław Pitucha BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.IO;
using System.Xml;
using System.Text;

namespace VeohDownloaderSharp
{
	public class VeohMovieUnavailableException : Exception {
	}
	
	public class VeohMovie
	{
		private string _vid;
		private long _size;
		private string _fileHash;
		private string[] _pieces;
		private string _title;
		private string _extension;
		private string _cacheRoot;
		
		public VeohMovie(string vid)
		{
			_vid = vid;
		}
		
		private void _parseMediaInfo(XmlDocument xd) {
			if(xd.SelectSingleNode("/Response/QueueEntry")==null)
				throw new VeohMovieUnavailableException();
		
			_fileHash = xd.SelectSingleNode("/Response/QueueEntry/Video/FileHash").InnerText.Trim();
			_size = long.Parse(xd.SelectSingleNode("/Response/QueueEntry/Video/Size").InnerText.Trim());
			_title = xd.SelectSingleNode("/Response/QueueEntry/Video/Title").InnerText.Trim();
			_extension = xd.SelectSingleNode("/Response/QueueEntry/Video/Extension").InnerText.Trim();
		}
		
		private XmlDocument _pullMediaInfo() {
			XmlDocument xd;
			string cachePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp/.cache."+_vid;
			
			if(File.Exists(cachePath)) {
				xd = new XmlDocument();
				xd.Load(cachePath);
				return xd;
			}
			
			VeohRequest vr = new VeohRequest();
			vr.Url = "http://www.veoh.com/service/getMediaInfo.xml?clientGUID=fZOXEICKQU8f16JDGGco9DmR4NmLvNZr8fSYvzUh0KpMKCxU5ZMs8D7rxk3sBhCR&version=3.2.0";
			vr.Data = String.Format("<MediaIdList><MediaId permalinkId=\"{0}\"/></MediaIdList>", _vid);
			xd = vr.RequestXml();
			
			if(!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp")) {
				Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp");
			}
			
			xd.Save(cachePath);
			
			return xd;
		}
		
		private XmlDocument _pullPiecesInfo() {
			if(_fileHash==null)
				GetInfo();

			XmlDocument xd;
			string cachePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp/.pieces."+_fileHash;
			
			if(File.Exists(cachePath)) {
				xd = new XmlDocument();
				xd.Load(cachePath);
				return xd;
			}
			
			VeohRequest vr = new VeohRequest();
			vr.Url = "http://tracker.veoh.com/tracker/search.jsp";
			vr.Data = String.Format("<request><peer id=\"B8B5568EC0B544B2A4137C805862DEF5\" port=\"7028\" /><wantPieces /><file id=\"{0}\" /></request>", _fileHash);
			xd = vr.RequestXml();
			
			if(!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp")) {
				Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"/VeohDownloaderSharp");
			}
			
			xd.Save(cachePath);

			return xd;
		}

		private int _getPieces() {
			XmlDocument xd = _pullPiecesInfo();
			
			XmlNodeList nl = xd.SelectNodes("/response/file/piece/@id");
			
			_pieces = new string[nl.Count];
			for(int i=0;i<nl.Count;i++) {
				_pieces[i]=nl[i].Value;
			}

			_cacheRoot = xd.SelectSingleNode("/response/file/root/@url").Value;
			
			return nl.Count;
		}
		
		public void GetInfo() {
			XmlDocument xd = _pullMediaInfo();
			_parseMediaInfo(xd);
		}
		
		public void GetPieces() {
			_getPieces();
		}
		
		public string Vid {
			get {
				return _vid;
			}
		}
		
		public long Size {
			get {
				return _size;
			}
		}
		
		public string FileHash {
			get {
				return _fileHash;
			}
		}
		
		public string[] Pieces {
			get {
				return _pieces;
			}
		}
		
		public string Title {
			get {
				return _title;
			}
		}
		
		public string Extension {
			get {
				return _extension;
			}
		}
		
		public void DownloadFile() {
			if(_pieces==null)
				GetPieces();
				
			using (FileStream writer = new FileStream(_title+_extension, FileMode.OpenOrCreate)) {
				if(writer.Length>=_size) {
					Console.WriteLine("File is already complete");
					return;
				}
				
				long i;
				if(writer.Length==0)
					i=0;
				else
					i=writer.Length/(256*1024);
				writer.Position = i*256*1024;
			
				byte[] buffer = new byte[1024*8];
				
				for(;i<_pieces.Length;i++) {
					string url = String.Format("{0}/get.jsp?fileHash={1}&pieceHash={2}", _cacheRoot, _fileHash, _pieces[i]);
					
					HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
					using(HttpWebResponse resp = (HttpWebResponse) req.GetResponse()) {
						using(Stream reader = resp.GetResponseStream()) {
							int bytes;
							while((bytes=reader.Read(buffer, 0, 1024*8))>0) {
								Console.Write('.');
								writer.Write(buffer, 0, bytes);
							}
						}
					}
					
					Console.WriteLine("Piece no. {0} finished ({1}% - {2:0.0}MB/{3:0.0}MB)", i, writer.Position*100/_size, (double)writer.Position/1024/1024, (double)_size/1024/1024);
				}
				writer.Flush();
				Console.WriteLine("Download complete");
			}
		}
	}
}
